(function(){
	'use strict';

	angular.module( 'ngAuth', [] )
	
	.factory( 'TokenService', function(){
		return{ token: undefined }					/* Auth token to be injected in every request */
	} )
	
	.provider( 'Auth', function(){
	
		var	loginUrl = undefined,					/* Server Login URL */
			logoutUrl = undefined,					/* Server Logout URL */
			withLog = false,						/* Recommended for development enviroment only */
			userNameParam = 'user',					/* Name of user param in rootScope */
			tokenHeaderName = 'x-session-token',	/* Header name for auth token */
			authRouteParamName = 'auth',			/* Name of route param */
			cancelAuthOnRoute = undefined;			/* '$routeChangeStart' listener destroyer */
		
		return {
			
			/* 
			 * Sets configuration for our provider 
			 */
			setConfig: function( cfg ){
				loginUrl 			= cfg.loginUrl 				|| loginUrl;
				logoutUrl 			= cfg.logoutUrl 			|| logoutUrl;
				tokenHeaderName 	= cfg.tokenHeaderName 		|| tokenHeaderName;
				authRouteParamName 	= cfg.authRouteParamName 	|| authRouteParamName;
				withLog				= cfg.withLog				|| withLog;
				userNameParam		= cfg.userNameParam			|| userNameParam;
			},
			
			/* 
			 * Return injector to send token ( if set ) in every request
			 */
			getTokenInjector: function(){
				return [ 'TokenService', function( TokenService ){
					return{
			            request: function( config ) {
			                if( TokenService.token )
			                    config.headers[tokenHeaderName] = TokenService.token;
			                return config;
			            }
					}
		        } ];
			},
			/*
			 * Auth Service
			 */
			$get: [ '$http', '$rootScope', '$window', '$log', '$route', '$location', 'TokenService',
			        function( $http, $rootScope, $window, $log, $route, $location, TokenService ){
				
				/* 
				 * ngRoute Functions
				 * 
				 * Functions extracted from ngRoute to make them public. 
				 * This is a known issue and this functions will be removed when ngRoute make them public natively
				 */
				function parseRoute() {
                    // Match a route
                    var params, match;
                    angular.forEach($route.routes, function(route, path) {
                        if (!match && (params = switchRouteMatcher($location.path(), route))) {
                            match = inherit(route, {
                                params: angular.extend({}, $location.search(), params),
                                pathParams: params});
                            match.$$route = route;
                        }
                    });
                    // No route matched; fallback to "otherwise" route
                    return match || $route.routes[null] && inherit($route.routes[null], {params: {}, pathParams:{}});
                }
				
				function switchRouteMatcher(on, route) {
                    var keys = route.keys,
                        params = {};

                    if (!route.regexp) return null;

                    var m = route.regexp.exec(on);
                    if (!m) return null;

                    for (var i = 1, len = m.length; i < len; ++i) {
                        var key = keys[i - 1];

                        var val = 'string' == typeof m[i]
                            ? decodeURIComponent(m[i])
                            : m[i];

                        if (key && val) {
                            params[key.name] = val;
                        }
                    }
                    return params;
                }
                
				function inherit(parent, extra) {
                    return angular.extend(new (angular.extend(function() {}, {prototype:parent}))(), extra);
                }
				
				/* 
				 * Init. authorization when routing
				 */
				cancelAuthOnRoute = $rootScope.$on( '$locationChangeStart', function( event ) {
					var nextRoute = parseRoute();
					if( nextRoute[authRouteParamName] === true ){
						if( $rootScope[userNameParam] === undefined ){
							if( withLog ) $log.warn( 'Unauthorized access' );
							event.preventDefault();
						}
					}
                } );
				
				return {
					
					login: function( credentials, onSuccess, onError ) {
						if( loginUrl ){
							$http( { method: 'POST', url: loginUrl, params: credentials } )
							.success( function( data ){
								isLogged = true;
								if( withLog ) $log.info( 'Login Success' );
								if( withLog ) $log.info( 'Authorization on routing added.' );
								if( onSuccess ) onSuccess( data );
							} )
							.error( function( data ){
								if( withLog ) $log.error( 'Login Error' );
								if( onError ) onError( data );
							} );
						}else{
							if( withLog ) 
								$log.error( 'Login Path has not been defined in module.config(). Use "AuthProvider.setConfig( { loginUrl : "/example/path" } )"' );
						}
					},
					
					logout: function( onSuccess, onError ){
						if( logoutUrl ){
							$http( { method: 'POST', url: logoutUrl } )
							.success( function( data ){
								isLogged = false;
								if( withLog ) $log.info( 'Logout Success' );
								if( withLog ) $log.info( 'Authorization on routing removed.' );
								if( onSuccess ) onSuccess( data );
							} )
							.error( function( data ){
								if( withLog ) $log.error( 'Logout Error' );
								if( onError ) onError( data );
							} );
						}else{
							if( withLog ) 
								$log.error( 'Logout Path has not been defined in module.config(). Use "AuthProvider.setConfig( { logoutUrl : "/example/path" } )"' );
						}
					},
					
					setAuthToken: function( newToken ){
						TokenService.token = newToken; return TokenService.token;
					},
					
				}
				
			} ]
		};
	} );
	
})();